package ch15
{
    
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.ui.Keyboard;
    
    import utils.Tree;
    
    /**
     *
     * @author jianping.shenjp
     *
     * 屏幕环绕
     * 回忆一下第六章，我们说过三种当物体碰到边界后受到反作用力的可能。
     * 目前为止，我们只介绍了反弹。还有两个：屏幕环绕与重置。
     * 对于 3D 而言，我发现屏幕环绕效果是最为有用的，但只能在 z 轴上使用。
     *
     * 2D 屏幕环绕中，在 x 或 y 轴上判断物体是否出了屏幕。
     * 效果非常好，因为当物体超出了其中一个边界时就看不到它了，因此可以轻松地重新设置物体的位置，
     * 不会引起人们的注意。但是 3D 中就不能这么潇洒了。
     *
     * 在 3D 中，实际上只有两个点可以安全地删除和重置物体。
     * 一个就是当物体运动到观察点的后面。前面例子中，将物体设置为不可见时，就是这个道理。
     * 另一个就是当物体的距离太远和太小时也可以将其设为不可见的。
     * 这就意味着我们可以在 z 轴上安全地进行屏幕包装。当物体走到身后时，就将它放到面前的远方。
     * 如果物体离得过远，超出了可见范围，就可以删除它并将其重置到身后。如果大家喜欢 x, y 轴也可以这样做，
     * 但是多数情况下，这么做会导致一些不自然的忽隐忽现的效果。
     *
     * 主体思想是把不同的 3D 物体放到观察点前。然后将这些物体向观察点移动。
     * 换句话讲，给它们一些负的 z 速度。这要看我们如何设置了，可以让物体向我们走来，让眼睛以为是我们向物体走去。
     * 一旦物体走到了观察点后，就将它重置到眼前一段距离。这样就可以永无止境地掠过这些物体了。
     */
    [SWF(backgroundColor=0x000000)]
    public class Trees extends Sprite
    {
        private var trees:Array;
        private var numTrees:uint = 100;
        private var fl:Number = 250;
        //消失点（vanishing point）vpX, vpY
        private var vpX:Number = stage.stageWidth / 2;
        private var vpY:Number = stage.stageHeight / 2;
        //地板位置
        private var floor:Number = 50;
        private var vz:Number = 0;
        //摩擦力
        private var friction:Number = 0.98;
        
        public function Trees()
        {
            init();
        }
        
        private function init():void
        {
            trees = new Array();
            for (var i:uint = 0; i < numTrees; i++)
            {
                var tree:Tree = new Tree();
                trees.push(tree);
                tree.xpos = Math.random() * 2000 - 1000;
                tree.ypos = floor;
                tree.zpos = Math.random() * 10000;
                addChild(tree);
            }
            addEventListener(Event.ENTER_FRAME, onEnterFrame);
            stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
        }
        
        private function onEnterFrame(event:Event):void
        {
            for (var i:uint = 0; i < numTrees; i++)
            {
                var tree:Tree = trees[i];
                move(tree);
            }
            vz *= friction;
            sortZ();
        }
        
        private function onKeyDown(event:KeyboardEvent):void
        {
            if (event.keyCode == Keyboard.UP)
            {
                vz -= 1;
            }
            else if (event.keyCode == Keyboard.DOWN)
            {
                vz += 1;
            }
        }
        
        private function move(tree:Tree):void
        {
            tree.zpos += vz;
            //公式:scale = fl / (fl + z)
            /**
             * 当物体离屏幕很近时，即物体在观察点后面时候，将物体拉远；
             * 当物体距离太远时，将物体拉近。
             * */
            if (tree.zpos < -fl)
            {
                tree.zpos += 10000;
            }
            if (tree.zpos > 10000 - fl)
            {
                tree.zpos -= 10000;
            }
            var scale:Number = fl / (fl + tree.zpos);
            tree.scaleX = tree.scaleY = scale;
            tree.x = vpX + tree.xpos * scale;
            tree.y = vpY + tree.ypos * scale;
//			tree.alpha = scale ;
//            tree.alpha = scale * 0.7 + 0.3;
            tree.alpha = scale * 0.7;
        }
        
        private function sortZ():void
        {
            //按降序、数字排序
            trees.sortOn("zpos", Array.DESCENDING | Array.NUMERIC);
            for each (var tree:Tree in trees)
            {
                addChild(tree);
            }
        }
    }
}